import 'dart:convert';

import 'package:dio/dio.dart';
import 'package:flutter/foundation.dart';
import 'package:winkai/res/app_config.dart';
import 'package:winkai/res/constant.dart';
import 'package:winkai/dio/logging_interceptor.dart';

import 'base_result.dart';
import 'error_handle.dart';

class NetUtils {
  NetUtils._() {
    _dio = _createDioInstance();
  }

  static final NetUtils _singleton = NetUtils._();

  static NetUtils get instance => _singleton;

  late Dio _dio;

  Dio _createDioInstance() {
    var options = BaseOptions(
      connectTimeout: const Duration(seconds: 30),
      receiveTimeout: const Duration(seconds: 30),
      baseUrl: AppConfig.apiHost,
    );
    Dio dio = Dio(options);

    dio.interceptors.add(LoggingInterceptor());

    return dio;
  }

  // 数据返回格式统一，统一处理异常
  Future<BaseResult<T>> _request<T>(
    String method,
    String url, {
    Object? data,
    Map<String, dynamic>? queryParameters,
    CancelToken? cancelToken,
    Options? options,
    void Function(int, int)? onSendProgress,
  }) async {
    try {
      final Future<Response<String>> response = _dio.request<String>(
        url,
        data: data,
        queryParameters: queryParameters,
        options: _checkOptions(method, options),
        cancelToken: cancelToken,
        onSendProgress: onSendProgress,
      );

      response.catchError((e, stack) {
        FlutterError.reportError(FlutterErrorDetails(
          exception: e,
          stack: stack,
          library: 'dio library',
        ));
      });

      final String responseData = (await response).data.toString();

      /// 集成测试无法使用 isolate https://github.com/flutter/flutter/issues/24703
      /// 使用compute条件：数据大于10KB（粗略使用10 * 1024）且当前不是集成测试（后面可能会根据Web环境进行调整）
      /// 主要目的减少不必要的性能开销
      final bool isCompute = responseData.length > 10 * 1024;
      final dynamic map = isCompute
          ? await compute(parseData, responseData)
          : parseData(responseData);
      return BaseResult<T>.fromJsonNoBody(map);
    } catch (e) {
      debugPrint(e.toString());
      final NetError error = ExceptionHandle.handleException(e);
      return BaseResult<T>(error.code, error.msg, null);
    }
  }

  Options _checkOptions(String method, Options? options) {
    options ??= Options();
    options.method = method;
    return options;
  }

  Future<BaseResult<T>> requestNetwork<T>(
    Method method,
    String url, {
    Object? params,
    Map<String, dynamic>? queryParameters,
    CancelToken? cancelToken,
    Options? options,
    bool autoShowErrorTips = false,
    void Function(int, int)? onSendProgress,
  }) {
    return _request<T>(
      method.value,
      url,
      data: params,
      queryParameters: queryParameters,
      options: options,
      cancelToken: cancelToken,
      onSendProgress: onSendProgress,
    );
  }
}

dynamic parseData(String data) {
  return json.decode(data);
}

enum Method { get, post, put, patch, delete, head }

extension MethodExtension on Method {
  String get value => ['GET', 'POST', 'PUT', 'PATCH', 'DELETE', 'HEAD'][index];
}
